//############################################################################
//## WiiDS Emulator (DeSmuME Port)
//##--------------------------------------------------------------------------
//## preferences.cpp
//## - User Setting Saving/Loading.
//############################################################################

#include <gccore.h>
#include <stdio.h>
#include <string.h>
#include <sys/dir.h>
#include <ogcsys.h>
#include <mxml.h>

#include "wiids.h"
#include "menu.h"
#include "fileop.h"
#include "filebrowser.h"
//#include "input.h"
//#include "button_mapping.h"

struct SSettings Settings;
static mxml_node_t *xml = NULL;
static mxml_node_t *data = NULL;
static mxml_node_t *section = NULL;
static mxml_node_t *item = NULL;
//static mxml_node_t *elem = NULL;

//----------------------------------------------------------------------------
// PREFS - mxmlFindNewElement
//----------------------------------------------------------------------------
//static mxml_node_t *mxmlFindNewElement(mxml_node_t *parent, const char *nodename, const char *attr=NULL, const char *value=NULL)
//{
//	mxml_node_t *node = mxmlFindElement(parent, xml, nodename, attr, value, MXML_DESCEND);
//	if (!node)
//	{
//		node = mxmlNewElement(parent, nodename);
//		if (attr && value) mxmlElementSetAttr(node, attr, value);
//	}
//	return node;
//}

static char temp[20];

//----------------------------------------------------------------------------
// PREFS - toStr
//----------------------------------------------------------------------------
static const char * toStr(int i)
{
	sprintf(temp, "%d", i);
	return temp;
}

//----------------------------------------------------------------------------
// PREFS - toHex
//----------------------------------------------------------------------------
//static const char * toHex(u32 i)
//{
//	sprintf(temp, "0x%06X", i);
//	return temp;
//}

//----------------------------------------------------------------------------
// PREFS - FtoStr
//----------------------------------------------------------------------------
//static const char * FtoStr(float i)
//{
//	sprintf(temp, "%.2f", i);
//	return temp;
//}

//----------------------------------------------------------------------------
// PREFS - createXMLSection
//----------------------------------------------------------------------------
static void createXMLSection(const char * name, const char * description)
{
	section = mxmlNewElement(data, "section");
	mxmlElementSetAttr(section, "name", name);
	mxmlElementSetAttr(section, "description", description);
}

//----------------------------------------------------------------------------
// PREFS - createXMLSetting
//----------------------------------------------------------------------------
static void createXMLSetting(const char * name, const char * description, const char * value)
{
	item = mxmlNewElement(section, "setting");
	mxmlElementSetAttr(item, "name", name);
	mxmlElementSetAttr(item, "value", value);
	mxmlElementSetAttr(item, "description", description);
}

//----------------------------------------------------------------------------
// PREFS - createXMLController
//----------------------------------------------------------------------------
//static void createXMLController(unsigned int controller[], const char * name, const char * description)
//{
//	item = mxmlNewElement(section, "controller");
//	mxmlElementSetAttr(item, "name", name);
//	mxmlElementSetAttr(item, "description", description);
	// create buttons
//	for(int i=0; i < MAXJP; i++)
//	{
//		elem = mxmlNewElement(item, "button");
//		mxmlElementSetAttr(elem, "number", toStr(i));
//		mxmlElementSetAttr(elem, "assignment", toStr(controller[i]));
//	}
//}

//----------------------------------------------------------------------------
// PREFS - XMLSaveCallback
//----------------------------------------------------------------------------
static const char * XMLSaveCallback(mxml_node_t *node, int where)
{
	const char *name;

	name = node->value.element.name;

	if(where == MXML_WS_BEFORE_CLOSE)
	{
		if(!strcmp(name, "file") || !strcmp(name, "section"))
			return ("\n");
//		else if(!strcmp(name, "controller"))
//			return ("\n\t");
	}
	if (where == MXML_WS_BEFORE_OPEN)
	{
		if(!strcmp(name, "file"))
			return ("\n");
		else if(!strcmp(name, "section"))
			return ("\n\n");
//		else if(!strcmp(name, "setting") || !strcmp(name, "controller"))
		else if(!strcmp(name, "setting"))
			return ("\n\t");
//		else if(!strcmp(name, "button"))
//			return ("\n\t\t");
	}
	return (NULL);
}

//----------------------------------------------------------------------------
// PREFS - preparePrefsData
//----------------------------------------------------------------------------
static int preparePrefsData()
{
	xml = mxmlNewXML("1.0");
	mxmlSetWrapMargin(0); // disable line wrapping

	data = mxmlNewElement(xml, "file");
	mxmlElementSetAttr(data, "app", APPNAME);
	mxmlElementSetAttr(data, "version", APPVERSION);

	// File Settings
	createXMLSection("File", "File Settings");
	createXMLSetting("AutoLoad", "Auto Load", toStr(Settings.AutoLoad));
	createXMLSetting("AutoSave", "Auto Save", toStr(Settings.AutoSave));
	createXMLSetting("LoadMethod", "Load Method", toStr(Settings.LoadMethod));
	createXMLSetting("SaveMethod", "Save Method", toStr(Settings.SaveMethod));
	createXMLSetting("LoadFolder", "ROM Folder", Settings.LoadFolder);
	createXMLSetting("SaveFolder", "Save Folder", Settings.SaveFolder);

	// Video Settings
	createXMLSection("Video", "Video Settings");
	createXMLSetting("Videomode", "Video Mode", toStr(Settings.Videomode));
	createXMLSetting("Filtermode", "Video Filtering Mode", toStr(Settings.Filtermode));
	createXMLSetting("VidScaling", "Video Scaling", toStr(Settings.VidScaling));

	// Menu Settings
	createXMLSection("Menu", "Menu Settings");
	createXMLSetting("MusicVolume", "Music Volume", toStr(Settings.MusicVolume));
	createXMLSetting("SFXVolume", "Sound FX Volume", toStr(Settings.SFXVolume));
	createXMLSetting("Rumble", "Rumble", toStr(Settings.Rumble));
//	createXMLSetting("language", "Language", toStr(Settings.language));

	// Controller Settings
//	createXMLSection("Controller", "Controller Settings");
//	createXMLController(btnmap[CTRLR_WIIMOTE], "wmpadmap", "Wiimote");
//	createXMLController(btnmap[CTRLR_NUNCHUK], "ncpadmap", "Nunchuk");
//	createXMLController(btnmap[CTRLR_GCPAD], "gcpadmap", "GameCube Pad");
//	createXMLController(btnmap[CTRLR_CLASSIC], "ccpadmap", "Classic Controller");

	int datasize = mxmlSaveString(xml, (char *)savebuffer, SAVEBUFFERSIZE, XMLSaveCallback);

	mxmlDelete(xml); // Clear Old Settings
	return datasize; // Return New Settings
}

//----------------------------------------------------------------------------
// PREFS - loadXMLSetting
//----------------------------------------------------------------------------
static void loadXMLSetting(char * var, const char * name, int maxsize)
{
	item = mxmlFindElement(xml, xml, "setting", "name", name, MXML_DESCEND);
	if(item)
	{
		const char * tmp = mxmlElementGetAttr(item, "value");
		if(tmp)
			snprintf(var, maxsize, "%s", tmp);
	}
}
static void loadXMLSetting(int * var, const char * name)
{
	item = mxmlFindElement(xml, xml, "setting", "name", name, MXML_DESCEND);
	if(item)
	{
		const char * tmp = mxmlElementGetAttr(item, "value");
		if(tmp)
			*var = atoi(tmp);
	}
}
//static void loadXMLSetting(float * var, const char * name)
//{
//	item = mxmlFindElement(xml, xml, "setting", "name", name, MXML_DESCEND);
//	if(item)
//	{
//		const char * tmp = mxmlElementGetAttr(item, "value");
//		if(tmp)
//			*var = atof(tmp);
//	}
//}

//----------------------------------------------------------------------------
// PREFS - loadXMLController
//----------------------------------------------------------------------------
//static void loadXMLController(unsigned int controller[], const char * name)
//{
//	item = mxmlFindElement(xml, xml, "controller", "name", name, MXML_DESCEND);
//	if(item)
//	{
//		for(int i=0; i < MAXJP; i++)
//		{
//			elem = mxmlFindElement(item, xml, "button", "number", toStr(i), MXML_DESCEND);
//			if(elem)
//			{
//				const char * tmp = mxmlElementGetAttr(elem, "assignment");
//				if(tmp)
//					controller[i] = atoi(tmp);
//			}
//		}
//	}
//}

//----------------------------------------------------------------------------
// PREFS - decodePrefsData
//----------------------------------------------------------------------------
static bool decodePrefsData ()
{
	bool result = false;

	xml = mxmlLoadString(NULL, (char *)savebuffer, MXML_TEXT_CALLBACK);

	if(xml)
	{
		// check settings version
		item = mxmlFindElement(xml, xml, "file", "version", NULL, MXML_DESCEND);
		if(item)
		{
			const char * version = mxmlElementGetAttr(item, "version");
			if(version && strlen(version) == 5)
			{
				// this code assumes version in format X.X.X
				// XX.X.X, X.XX.X, or X.X.XX will NOT work
				int verMajor = version[0] - '0';
				int verMinor = version[2] - '0';
				int verPoint = version[4] - '0';
				int curMajor = APPVERSION[0] - '0';
				int curMinor = APPVERSION[2] - '0';
				int curPoint = APPVERSION[4] - '0';
				// we check that the versioning is valid
				if(!(verMajor >= 0 && verMajor <= 9 &&
					verMinor >= 0 && verMinor <= 9 &&
					verPoint >= 0 && verPoint <= 9))
					result = false;
//				else if(verMajor < 1) // Less than 1.0.0
//					result = false; // reset settings
				else if((verMajor*100 + verMinor*10 + verPoint) > (curMajor*100 + curMinor*10 + curPoint)) // Newer Version
					result = false; // reset settings
				else
					result = true;
			}
		}

		if(result)
		{
			// File Settings
			loadXMLSetting(&Settings.AutoLoad, "AutoLoad");
			loadXMLSetting(&Settings.AutoSave, "AutoSave");
			loadXMLSetting(&Settings.LoadMethod, "LoadMethod");
			loadXMLSetting(&Settings.SaveMethod, "SaveMethod");
			loadXMLSetting(Settings.LoadFolder, "LoadFolder", sizeof(Settings.LoadFolder));
			loadXMLSetting(Settings.SaveFolder, "SaveFolder", sizeof(Settings.SaveFolder));

			// Video Settings
			loadXMLSetting(&Settings.Videomode, "Videomode");
			loadXMLSetting(&Settings.Filtermode, "Filtermode");
			loadXMLSetting(&Settings.VidScaling, "VidScaling");

			// Menu Settings
			loadXMLSetting(&Settings.MusicVolume, "MusicVolume");
			loadXMLSetting(&Settings.SFXVolume, "SFXVolume");
			loadXMLSetting(&Settings.Rumble, "Rumble");
//			loadXMLSetting(&Settings.language, "language");

			// Controller Settings
//			loadXMLController(btnmap[CTRLR_WIIMOTE], "wmpadmap");
//			loadXMLController(btnmap[CTRLR_NUNCHUK], "ncpadmap");
//			loadXMLController(btnmap[CTRLR_GCPAD], "gcpadmap");
//			loadXMLController(btnmap[CTRLR_CLASSIC], "ccpadmap");
		}
		mxmlDelete(xml); // Clear Old Settings
	}
	return result; // Return New Settings
}

//----------------------------------------------------------------------------
// PREFS - FixInvalidSettings
//----------------------------------------------------------------------------
void FixInvalidSettings()
{
	if(Settings.LoadMethod > 3)
		Settings.LoadMethod = DEVICE_AUTO;
	if(Settings.SaveMethod > 3)
		Settings.SaveMethod = DEVICE_AUTO;
	if(!(Settings.MusicVolume >= 0 && Settings.MusicVolume <= 100))
		Settings.MusicVolume = 60;
	if(!(Settings.SFXVolume >= 0 && Settings.SFXVolume <= 100))
		Settings.SFXVolume = 80;
//	if(Settings.language < 0 || Settings.language > LANG_KOREAN)
//		Settings.language = LANG_ENGLISH;
	if(!(Settings.Filtermode >= 0 && Settings.Filtermode < 3))
		Settings.Filtermode = 1;
	if(!(Settings.Videomode >= 0 && Settings.Videomode < 5))
		Settings.Videomode = 0;
}

//----------------------------------------------------------------------------
// PREFS - DefaultSettings
//----------------------------------------------------------------------------
void DefaultSettings()
{
	memset (&Settings, 0, sizeof (Settings));
//	ResetControls(); // Reset Button Maps

	Settings.AutoLoad = 1;
	Settings.AutoSave = 1;
	Settings.LoadMethod = DEVICE_AUTO;
	Settings.SaveMethod = DEVICE_AUTO;
	sprintf (Settings.LoadFolder, "%s/ROMS/", APPFOLDER);
	sprintf (Settings.SaveFolder, "%s/SAVES/", APPFOLDER);
	sprintf (Settings.CoverFolder, "%s/COVERS/", APPFOLDER);
	Settings.MusicVolume = 60;
	Settings.SFXVolume = 80;
	Settings.Videomode = 0;
	Settings.VidScaling = 1;
	Settings.Filtermode = 1;
	Settings.Rumble = 1;

//	Settings.Language = GetLanguage();
//	if(Settings.language == LANG_DE || 
//		Settings.language == LANG_FR || 
//		Settings.language == LANG_SP || 
//		Settings.language == LANG_IT || 
//		Settings.language == LANG_RU || 
//		Settings.language == LANG_CHS || 
//		Settings.language == LANG_CHT || 
//		Settings.language == LANG_JP || 
//		Settings.language == LANG_TH || 
//		Settings.language == LANG_KR)
//		Settings.language = LANG_EN;
}

//----------------------------------------------------------------------------
// PREFS - SavePrefs
//----------------------------------------------------------------------------
static char prefpath[MAXPATHLEN] = { 0 };
bool SavePrefs (bool silent)
{
	char filepath[MAXPATHLEN];
	int datasize;
	int offset = 0;
	int device = 0;
	
	if(prefpath[0] != 0)
	{
		sprintf(filepath, "%s/%s", prefpath, PREF_FILE_NAME);
		FindDevice(filepath, &device);
	}
	else if(appPath[0] != 0)
	{
		sprintf(filepath, "%s/%s", appPath, PREF_FILE_NAME);
		strcpy(prefpath, appPath);
		FindDevice(filepath, &device);
	}
	else
	{
		device = autoSaveMethod(silent);
		
		if(device == 0)
			return false;
		
		sprintf(filepath, "%s%s", pathPrefix[device], APPFOLDER);
						
		if (!diropen(filepath))
		{
			mkdir(filepath, 0777);
			sprintf(filepath, "%s%s/roms", pathPrefix[device], APPFOLDER);
			mkdir(filepath, 0777);
			sprintf(filepath, "%s%s/saves", pathPrefix[device], APPFOLDER);
			mkdir(filepath, 0777);
		}
		sprintf(filepath, "%s%s/%s", pathPrefix[device], APPFOLDER, PREF_FILE_NAME);
		sprintf(prefpath, "%s%s", pathPrefix[device], APPFOLDER);
	}
	
	if(device == 0)
		return false;

	if (!silent)
		ShowAction ("Saving preferences...");

	FixInvalidSettings();

	AllocSaveBuffer ();
	datasize = preparePrefsData ();

	offset = SaveFile(filepath, datasize, silent);

	FreeSaveBuffer ();

	CancelAction();

	if (offset > 0)
	{
		if (!silent)
			InfoPrompt("Preferences saved");
		return true;
	}
	return false;
}

//----------------------------------------------------------------------------
// PREFS - LoadPrefsFromMethod
//----------------------------------------------------------------------------
bool LoadPrefsFromMethod (char * path)
{
	bool retval = false;
	int offset = 0;
	char filepath[MAXPATHLEN];
	sprintf(filepath, "%s/%s", path, PREF_FILE_NAME);

	AllocSaveBuffer();

	offset = LoadFile(filepath, SILENT);

	if (offset > 0)
		retval = decodePrefsData ();

	FreeSaveBuffer ();

	if(retval)
	{
		strcpy(prefpath, path);

		if(appPath[0] == 0)
			strcpy(appPath, prefpath);
	}

	return retval;
}

//----------------------------------------------------------------------------
// PREFS - LoadPrefs
//----------------------------------------------------------------------------
static bool prefLoaded = false;
bool LoadPrefs()
{
	if(prefLoaded) // already attempted loading
		return true;

	bool prefFound = false;
	char filepath[4][MAXPATHLEN];
	int numDevices;
	
	numDevices = 5;
	sprintf(filepath[0], "%s", appPath);
	sprintf(filepath[1], "sd:/apps/%s", APPFOLDER);
	sprintf(filepath[2], "usb:/apps/%s", APPFOLDER);
	sprintf(filepath[3], "sd:/%s", APPFOLDER);
	sprintf(filepath[4], "usb:/%s", APPFOLDER);

	for(int i=0; i<numDevices; i++)
	{
		prefFound = LoadPrefsFromMethod(filepath[i]);
		if(prefFound)
			break;
	}

	prefLoaded = true; // attempted to load preferences

	if(prefFound)
		FixInvalidSettings();

//	ResetText();
	return prefFound;
}
